package lol.clann.bukkit.pluginboot.util;

import lol.clann.clannboot.annotation.svc.PostInstance;
import lol.clann.clannboot.annotation.svc.Service;
import lol.clann.clannboot.util.clazz.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;

import java.util.UUID;
import java.util.function.Predicate;

/**
 * @author pyz
 * @date 2018/8/5 上午7:45
 */
@Service(priority = Integer.MAX_VALUE)
public class WorldUtils {

    public static final WrapClass classNMSWorldServer;
    public static final WrapClass classNMSWorld;
    public static final WrapClass classCraftWorld;
    public static final WrapClass classVec3D;
    public static final WrapClass classIDataManager;
    public static final WrapClass classMovingObjectPosition;

    private WrapField world;
    private WrapMethod getTileEntity;
    private WrapMethod addTileEntity;
    private WrapMethod getSaveHandler;
    private WrapMethod rayTraceBlocks;
    private WrapMethod<UUID> getUUID;

    static {
        classNMSWorldServer = ClassUtils.forName("{nms}.WorldServer");
        classNMSWorld = ClassUtils.forName("{nms}.World");
        classCraftWorld = ClassUtils.forName("{cb}.CraftWorld");
        classVec3D = ClassUtils.forName("{nms}.Vec3D", "{nm}.util.math.Vec3D", "{nm}.util.math.Vec3d");
        classIDataManager = ClassUtils.forName("{nms}.IDataManager");
        classMovingObjectPosition = ClassUtils.forName("{nms}.MovingObjectPosition");
    }

    @PostInstance
    private void init() {
        world = classCraftWorld.getField("world");
        getTileEntity = classNMSWorldServer.getMethod("getTileEntity");
        getSaveHandler = classNMSWorldServer.getMethod(MethodFilter.build().returnType(classIDataManager).emptyParameter());
        getUUID = getSaveHandler.getReturnType().getMethod("getUUID");
        rayTraceBlocks = classNMSWorldServer.getMethod(MethodFilter.build().name("rayTrace", "rayTraceBlocks").index(2));
    }

    public boolean isChunkLoaded(Location loc) {
        return loc.getWorld().isChunkLoaded(loc.getBlockX() >> 4, loc.getBlockZ() >> 4);
    }

    /**
     * 从世界中移除满足条件的实体
     *
     * @param world
     * @param test
     */
    public void removeEntities(World world, Predicate<Entity> test) {
        for (Entity entity : world.getEntities()) {
            if (test.test(entity)) {
                entity.remove();
            }
        }
    }

    public void addTileEntity(World w, Object tileEntity) {
        addTileEntity.invoke(toNMS(w), tileEntity);
    }

    public void addTileEntity(String w, Object tileEntity) {
        addTileEntity(getWorld(w), tileEntity);
    }

    public Object getTileEntity(World w, int x, int y, int z) {
        Object nms = this.world.get(w);
        return getTileEntity.invoke(nms, x, y, z);
    }

    public Object getTileEntity(Block block) {
        return getTileEntity(block.getWorld(), block.getX(), block.getY(), block.getZ());
    }

    public Object getTileEntity(String w, int x, int y, int z) {
        return getTileEntity(getWorld(w), x, y, z);
    }

    public World getWorld(String name) {
        return Bukkit.getWorld(name);
    }

    public Object toNMS(World world) {
        return world == null ? null : this.world.get(world);
    }

    public World toBukkit(Object nms) {
        UUID uuid = getUUID.invoke(getSaveHandler.invoke(nms));
        for (World world1 : Bukkit.getWorlds()) {
            if (world1.getUID().equals(uuid)) {
                return world1;
            }
        }
        throw new RuntimeException("nmsWorld转换为bukkitWorld失败");
    }

    /**
     * @param vec3D1
     * @param vec3D2
     * @param flag1
     * @param flag2
     * @param flag3
     * @return MovingObjectPosition
     */
    public Object rayTraceBlocks(Object vec3D1, Object vec3D2, boolean flag1, boolean flag2, boolean flag3) {
        return rayTraceBlocks.invoke(vec3D1, vec3D2, flag1, flag2, flag3);
    }
}
